//
//  System.cpp
//  Simulation
//
//  Created by Christopher Kemsley on 2/24/12.
//  Copyright (c) 2012 iAppolopolis. All rights reserved.
//

#include "System.hpp"
#include "RunLoop.hpp"
#include "Log.hpp"

using namespace Sim ;





System&	System :: shared()
	{
	static System s ;
	return s ;
	}



Interval const&	System :: currentTime () const
	{
	return mCurrentTime ;
	}


System :: System ()
:	mCurrentTime ()
,	mControllers()
	{
	
	}
System :: ~System ()
	{
	
	}

void	System :: runForDuration	( Interval const & duration )
	{
	Interval	end	=	mCurrentTime + duration ;
	
	while ( mCurrentTime < end )
		{
		RunLoop::currentLoop() . run() ;
		
		Interval	step		=	Interval::distantFuture ;
		bool		isZeroTime	=	false ;
		
		std::set<SystemController*>::iterator	here ;
		std::set<SystemController*>::iterator	end ;
		
		
		// Find the minimum step
		here	=	mControllers.begin() ;
		end		=	mControllers.end() ;
		while ( here != end )
			{
			SystemController * next = *(here++) ;
			
			Interval	min	=	next -> minimumTimeInterval() ;
			if ( min.isNegative() )
				{
				next -> minimumTimeInterval() ;
				ThrowException ( "System" , "SystemController said they needed a negative time interval to run "<<min , __FILE__,__LINE__  ) ;
				}
			
			Log::states("System.verbose") << "...this's interval: " << min ;
			
			if ( min == Interval::zeroTime )
				{
				Log::states("System.verbose") << "...zero-time" ;
				step = min ;
				isZeroTime = true ;
				break ;
				}
			else if ( min < step )
				{
				Log::states("System.verbose") << "...new min!" ;
				step = min ;
				}
			}
		
		
		// Make sure we had a step
		if ( step == Interval::distantFuture )
			return ;
		
		
		Interval	to		=	step + mCurrentTime ;
		if ( to.isNegative() )
			{
			ThrowException ( "System" , "Advanced from " << mCurrentTime << " to " << to << " - went to the land before time" , __FILE__,__LINE__  ) ;
			}
		if ( step == Interval::zeroTime )
			Log::states("System") << "Advancing zero-time step" ;
		else
			Log::states("System") << "Advancing from " << mCurrentTime << " to " << to ;
		
		
		// Tell everyone we're about to advance
		here	=	mControllers.begin() ;
		end		=	mControllers.end() ;
		while ( here != end )
			{
			SystemController * next = *(here++) ;
			next -> systemWillAdvance ( mCurrentTime , to , isZeroTime ) ;
			}
		
		
		// Advance!
		mCurrentTime = to ;
		
		
		// Tell everyone we just advanced
		here	=	mControllers.begin() ;
		end		=	mControllers.end() ;
		while ( here != end )
			{
			SystemController * next = *(here++) ;
			next -> systemDidAdvance ( mCurrentTime , to , isZeroTime ) ;
			}
		}
	}



SystemController :: SystemController ()
	{
	System::shared().mControllers . insert ( this ) ;
	}
